package dk.au.cs.bdsi.core.repositories;

import java.util.Deque;
import java.util.Map;


public abstract class AbstractRepository<K,V> implements Repository<K, V> {
	protected final Map<K, V> store;
	protected final Deque<V> deque;
	
	/**
	 * Instantiates the {@link AbstractRepository} with the given
	 * backing store.
	 * 
	 * @param store Backing store
	 */
	protected AbstractRepository(Map<K, V> store, Deque<V> deque) {
		this.store = store;
		this.deque = deque;
	}
	
	/**
	 * Generate new identifying key with the property, that
	 * there is no such key binding already, i.e. the key
	 * must be non-existing at return-time.
	 * 
	 * @return New non-existing key
	 */
	protected abstract K generateKey();
	
	/* (non-Javadoc)
	 * @see dk.au.cs.bdsi.mail.repositories.Repository#get(java.lang.Object)
	 */
	@Override
	public V get(K key) {
		return store.get(key);
	}

	/* (non-Javadoc)
	 * @see dk.au.cs.bdsi.mail.repositories.Repository#store(java.lang.Object, java.lang.Object)
	 */
	@Override
	public void store(K key, V value) {
		V old = store.get(key);
		if (old != null) {
			synchronized (deque) {
				deque.remove(old);
			}
		}
		store.put(key, value);
		queue(value);
	}

	/* (non-Javadoc)
	 * @see dk.au.cs.bdsi.mail.repositories.Repository#store(java.lang.Object)
	 */
	@Override
	public K store(V value) {
		K key = generateKey();
		store.put(key, value);
		queue(value);
		return key;
	}

	/* (non-Javadoc)
	 * @see dk.au.cs.bdsi.mail.repositories.Repository#remove(java.lang.Object)
	 */
	@Override
	public void remove(K key) {
		V value = store.remove(key);
		if (value != null) {
			synchronized (deque) {
				deque.remove(value);
			}
		}
	}

	/* (non-Javadoc)
	 * @see dk.au.cs.bdsi.mail.repositories.Repository#has(java.lang.Object)
	 */
	@Override
	public boolean has(K key) {
		return store.containsKey(key);
	}
	
	@Override
	public void queue(V t) {
		synchronized (deque) {
			deque.addLast(t);
		}
	}
	
	@Override
	public V dequeue() {
		synchronized (deque) {
			return deque.pollFirst();
		}
	}
}
